﻿// GDI绘图.cpp : 定义应用程序的入口点。
//

#include "pch.h"
#include "framework.h"
#include "GDI_绘图.h"
#include <wingdi.h>
#pragma comment(lib,"msimg32.lib")

#define MAX_LOADSTRING 100


/*
GDI绘图 ： 图形图像的显示
    GDI：
        Graphics Device Interface
        图形      设备      接口

    GUI:
        Graphics User Interface
        图形      用户  接口


    Device:底层一点
    Interface:函数
    Graphics:眼镜看到的都是图形


HDC: Handle of Device Context
        句柄      设备  上下文
    图形设备上下文句柄
    句柄：windows上各种对象（窗口 文件 设备） 的唯一代表
    图形设备：实现图形图像显示的对象
    上下文：    阅读      上下文     考虑时间因素

    字符界面基本单位为字符
    图形界面基本单位为像素
    像素：px   就是一个个点
    分辨率高：一个像素由更少的液晶来表示
    分辨率低：更多的液晶来表示一个像素

    程序中描述一个像素点：
        协议  约定的就叫协议
        RGB协议

    WM_PAINT    所有的绘图代码都先在这儿写

绘图：
    windows操作系统直接一个个像素画
    像素制游戏：  魂斗罗     因为之前的硬件贵，不管是存储设备 还是运算设备都贵，所以用像素来画

    先有个画家   画家手里由画笔和画布
        画家使用画笔 或画刷 画图形到画布上
        画布： hdc
        画笔： 本身就有的
        画家： 程序本身

    SetPixel

    通用编程模型：
        1.制作画笔or画刷
        2.画笔or画刷交给画家
        3.画家画画
        4.画家拿默认的画笔or画刷

    CreateSolidBrush    实心画刷
    CreateHatchBrush    各种形状的画刷


贴图：
    由各种类型的图片    图片文件
    BMP JPEG    PNG     CGI


BeginPaint  EndPaint    这两个函数是成双成对的

从窗口句柄获取绘图的句柄
    GetDC
    ReleaseDC
        记住 用完绘图句柄 一定要还回去


WM_PAINT    刷新
窗口大小改变  会刷新整个窗口



贴图：
    由各种类型的图片    图片文件
    BMP JPEG    PNG     CGI

    1.图片文件准备好
        1.1注意图片格式   可以用画图软件转换成png格式     文件格式要是png类型
        1.2注意路径
            相对路径    当前文件夹下  就直接写图片文件名就行
            绝对路径    从盘符出发

    2.加载图片：
        把图片数据从文件中弄到内存中来
        cpu不能直接运算   硬盘中的数据  cpu只能运算内存中的数据
        硬盘----> 内存
        函数：
            LoadImage
            LoadBitmap
    3.创建兼容dc
        CreateCompatibleDC
    4.把图片选到兼容dc中
        Selectobject
    5.把图片从兼容dc绘制到hdc中
        BitBlt              直接画
        TransparentBlt      等比例缩放


闪烁      多重缓冲解决闪烁问题原理：
            当前绘制流程：
                1.先画    背景
                2.画 红色火柴人
                3.画 蓝色火柴人
                4.画 彩色人
        有可能     画出背景之后  直接画蓝色火柴人    就会看起来闪烁

        解决办法    ： 创建一个兼容dc
            先绘制到兼容dc上
                1.画 背景
                2.画 红色火柴人
                3.画 蓝色火柴人
                4.画 彩色人
            把兼容dc绘制到hdc上
        这样每次只画一张整的图 就不会出现闪烁

    多级缓冲流程：
        1.准备图片文件
        2.加载图片
        3.创建兼容位图
            CreateCompatibleBitmap
        4.创建兼容dc
            CreateCompatibleDC
        5.兼容位图设置到兼容dc中
            SelectObject
        6.把图片选到兼容dc中
            SelectObject
        7.图片绘制到兼容dc中
            BitBlt
        8.兼容dc绘制到hdc中
            BitBlt              直接画
            TransparentBlt      等比例缩放

显示文字：
    TextOut

*/






// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);



void Draw(HWND hWnd);
int n = 0;




int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_GDI, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GDI));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int)msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GDI));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_GDI);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // 将实例句柄存储在全局变量中

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_TIMER:
        n++;
        if (n >= 8)
        {
            n = 0;
        }
        Draw(hWnd);

        break;
    case WM_CREATE:
        SetTimer(hWnd, 1111, 100, NULL);
        n = 0;

        break;
    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // 分析菜单选择:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 在此处添加使用 hdc 的任何绘图代码...

        Draw(hWnd);


        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}


void Draw(HWND hWnd)
{
    HDC hdc = GetDC(hWnd);

    //绘图
#if 0
            //使用默认画笔画画
    int x = 100;
    for (int i = 0; i < 100; i++)
    {

        SetPixel(hdc, x + i, 100, RGB(255, 0, 0));      //设置一个像素点的函数
    }


    //设置焦点：
    POINT pos;
    MoveToEx(hdc, 200, 100, &pos);
    LineTo(hdc, 200, 200);      //设置从焦点位置画线的函数，该函数中的参数是直线终点的坐标，默认直线的焦点是（0,0），可以设置焦点位置；执行完之后，焦点替换为该函数设置的终点坐标


    //画笔：

    //1.自制画笔：
    HPEN hSolidPen = CreatePen(PS_SOLID, 5, RGB(0, 0, 255));
    HPEN hDashPen = CreatePen(PS_DASH, 3, RGB(0, 255, 255));

    //2.把画笔交给画家
    HGDIOBJ HoldPen = SelectObject(hdc, hSolidPen);
    LineTo(hdc, 100, 200);

    HoldPen = SelectObject(hdc, hDashPen);
    LineTo(hdc, 100, 100);
#endif

    //画刷        各种形状
#if 0
    HBRUSH hSolidBush = CreateSolidBrush(RGB(255, 0, 0));
    HBRUSH hHatchBush1 = CreateHatchBrush(HS_CROSS, RGB(0, 255, 0));


    //画两个矩形：
    SelectObject(hdc, hSolidBush);
    Rectangle(hdc, 300, 100, 400, 200);        //矩形

    SelectObject(hdc, hHatchBush1);
    Rectangle(hdc, 500, 100, 600, 200);


    //画圆形：
    Ellipse(hdc, 700, 200, 800, 100);   //圆
    //Ellipse(hdc, 900, 200, 1000, 400);  //椭圆  太丑了

#endif


    //贴图    

#if 1
    //2.加载图片：
#if 0
    //文件方式加载图片文件：
    HANDLE hBkBitmap = LoadImage(hInst, L"bk.bmp", IMAGE_BITMAP, 1055, 667, LR_LOADFROMFILE);

    HANDLE hPeople1 = LoadImage(hInst, L"people1.bmp", IMAGE_BITMAP, 154, 209, LR_LOADFROMFILE);
    HANDLE hPeople2 = LoadImage(hInst, L"people2.bmp", IMAGE_BITMAP, 174, 212, LR_LOADFROMFILE);

    HANDLE hPeople = LoadImage(hInst, L"move1.bmp", IMAGE_BITMAP, 480, 216, LR_LOADFROMFILE);
#else
    //资源方式加载图片文件：
    HANDLE hBkBitmap = LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 1055, 667, LR_LOADTRANSPARENT);

    HANDLE hPeople1 = LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP2), IMAGE_BITMAP, 154, 209, LR_LOADTRANSPARENT);
    HANDLE hPeople2 = LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP3), IMAGE_BITMAP, 174, 212, LR_LOADTRANSPARENT);

    HANDLE hPeople = LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP4), IMAGE_BITMAP, 480, 216, LR_LOADTRANSPARENT);
#endif

    //3.创建兼容dc
    HDC hComDc = CreateCompatibleDC(hdc);

    HDC hComDc1 = CreateCompatibleDC(hdc);
    HDC hComDc2 = CreateCompatibleDC(hdc);

    HDC hComDc3 = CreateCompatibleDC(hdc);


    //4.把图片选到兼容dc中
    SelectObject(hComDc, hBkBitmap);

    SelectObject(hComDc1, hPeople1);
    SelectObject(hComDc2, hPeople2);

    SelectObject(hComDc3, hPeople);


    //5.把图片从兼容dc绘制到hdc中
    BitBlt(hdc, 0, 0, 1055, 667, hComDc, 0, 0, SRCCOPY);              //直接画
    BitBlt(hdc, 100, 100, 154, 209, hComDc1, 0, 0, SRCCOPY);              //直接画
    BitBlt(hdc, 600, 100, 174, 212, hComDc2, 0, 0, SRCCOPY);              //直接画

    //透明就要贴图的方式不一样 
    BitBlt(hdc, 800, 100, 60, 108, hComDc3, 60 * n, 108, SRCAND);
    BitBlt(hdc, 800, 100, 60, 108, hComDc3, 60 * n, 0, SRCPAINT);

    //TransparentBlt      //等比例缩放
#endif

#if 0
    //多级缓冲

    //1.准备图片文件

    //2.加载图片
    HANDLE hBkBitmap = LoadImage(hInst, L"bk.bmp", IMAGE_BITMAP, 1055, 667, LR_LOADFROMFILE);

    HANDLE hPeople1 = LoadImage(hInst, L"people1.bmp", IMAGE_BITMAP, 154, 209, LR_LOADFROMFILE);
    HANDLE hPeople2 = LoadImage(hInst, L"people2.bmp", IMAGE_BITMAP, 174, 212, LR_LOADFROMFILE);


    //3.创建兼容位图
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 1055, 667);


    //4.创建兼容dc
    HDC hComDc1 = CreateCompatibleDC(hdc);
    HDC hComDc2 = CreateCompatibleDC(hdc);


    //5.兼容位图设置到兼容dc中
    SelectObject(hComDc2, hBitmap);


    //6.把图片选到兼容dc中
    SelectObject(hComDc1, hBkBitmap);


    //7.图片绘制到兼容dc中
    BitBlt(hComDc2, 0, 0, 1055, 667, hComDc1, 0, 0, SRCCOPY);

    SelectObject(hComDc1, hPeople1);
    BitBlt(hComDc2, 300, 200, 154, 209, hComDc1, 0, 0, SRCCOPY);

    SelectObject(hComDc1, hPeople2);
    BitBlt(hComDc2, 600, 100, 174, 212, hComDc1, 0, 0, SRCCOPY);

    //8.兼容dc绘制到hdc中
    //BitBlt              //直接画
    TransparentBlt(hdc, 0, 0, GetSystemMetrics(SM_CXFULLSCREEN), GetSystemMetrics(SM_CYFULLSCREEN), hComDc2, 0, 0, 1055, 667, SRCCOPY);     //等比例缩放
    //使用该函数要引用头文件和库     #include <wingdi.h> \n  #pragma comment(lib,"msimg32.lib")
#endif

#if 0
    SetTextColor(hdc, RGB(255, 0, 0));      //设置字体颜色
    //HGDIOBJ hOldFont = GetStockObject(NULL);
    HFONT hFont = CreateFont(30, 10, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    SelectObject(hdc, hFont);

    //显示文字
    TextOut(hdc, 100, 100, L"李琼懿", wcslen(L"李琼懿"));

#endif


    ReleaseDC(hWnd, hdc);
}